Skip to main content

Value Contract Structure

Value Contract structure consists of the following parts:

Most Important
  • Composition - defines the data structure that is used to store data in the ledger database.
  • Services - defines the rules that are used to interact with the data in the ledger database.
  • Constructor - defines the constructor of the VC.
  • Data Model - defines the data types that are used to store records in compositions.
  • Permissions - defines the permissions that are required to execute the service.

Constructor

Defines the constructor of the VC. The constructor is called when the VC is deployed to the ledger database.

class [[inery::contract("valuecontract")]] valuecontract : public contract {
public:
using contract::contract;
valuecontract(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {}
~valuecontract() {}
};

Data Model

Defines the data structure of the VC. The data structure are custom types that are used to store data in the ledger database.
Support primitive types are :int, uint, float, double, string, bool, time_point, time_point_sec, block_timestamp, checksum256, checksum160, checksum512, public_key, signature, symbol, symbol_code, asset, extended_asset, name, account_name, permission_name Also supports custom types that are defined by the user and array of primitive or custom types.

struct  profile {
std::string name;
uint64_t age;
};

Compositions

Compositions are similar to tables in a relational database. They are used to store indexed data in the ledger database. The composition is defined by the [[inery::table("table_name")]] attribute. or simply TABLE table_name macro.

Each composition has a primary key that is used to uniquely identify the data in the composition. The primary key is defined by the primary_key attribute. In example below, the primary key is the id field. Also we used vector of profiles to store nested in array data.

[[inery::table("group")]] 
struct group {
uint64_t id;
string group_name;
vector<profile> profiles;

uint64_t primary_key() const { return id; }
};
typedef inery::multi_index<"group"_n, group> groups_table;

Services

Services are custom functions that can be used by the actions.
The services are defined by the [[inery::action("service_name")]] attribute. or simply ACTION service_name macro.

Service structrue :

  • [[inery::action("service_name")]] - defines the service name.
  • service_name(param_type param_name, ...) - defines the service name and entry parameters.
  • composition_name composition_instance(payer, scope) - defines the composition instance with payer and scope.
    • payer is the database that pays for the memory storage of the data in the composition.
    • scope is the account that owns the data in the composition.
  • Database Operations
    • find(primary_key) - finds the row in the composition with the given primary key.
    • emplace(payer, [&](auto& row) { ... }) - adds a new row to the composition.
    • modify(iterator, payer, [&](auto& row) { ... }) - modifies the row in the composition.
    • erase(iterator) - erases the row in the composition.
    • available_primary_key() - returns the next available primary key in the composition.

Create Composition Instance Example

[[inery::action("creategroup")]]
void creategroup(string group_name) {
groups_table groups(get_self(), get_self().value);
groups.emplace(get_self(), [&](auto& row) {
row.id = group.available_primary_key();
row.group_name = group_name;
});
}

Permissions

Permissions are required to execute the actions. Also before permission check, databse checks if the action is allowed to be executed by the user.

reqire_auth - checks if the action is allowed to be executed by the user. check - checks if the condition is true.

In the examaple below, the addprofile action requires the get_self() permission to execute. get_self() is the database that deployed the VC. Also, the action checks if the profile age is greater than 18 and if the group exists.

Update Composition Instance Example

[[inery::action("addprofile")]]
void addprofile(uint64_t group_id, profile new_profile) {
require_auth(get_self());
check(new_profile.age > 18, "Profile age must be greater than 18");
groups_table groups(get_self(), get_self().value);
auto group = groups.find(group_id);
check(group != groups.end(), "Group does not exist");
groups.modify(group, get_self(), [&](auto& row) {
row.profiles.push_back(new_profile);
});
}